/*
 * Copyright (c) 2020 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "securec.h"
#include "source.h"
#include "player.h"
#include "format.h"

#include <algorithm>
#include <cstring>
#include <fstream>
#include <iostream>
#include <sstream>

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>

using OHOS::Surface;

using namespace std;
using namespace OHOS;
using namespace OHOS::Media;

struct MyStreamCallback : StreamCallback {
public:
    uint8_t *GetBuffer(size_t index) {
        printf("----- GetBuffer(): i/%d ----- \n", index);
        return nullptr;
    }
    void QueueBuffer(size_t index, size_t offset, size_t size, int64_t timestampUs, uint32_t flags) {
        printf("----- QueueBuffer(): i/%d o/%d s/%d t/%d f/%d----- \n",
            index, offset, size, timestampUs, flags);
    }
    void SetParameters(const Format &params) {
        printf("----- SetParameters() ----- \n");
    }
};

typedef struct TagIdleBuffer{
    size_t idx;
    size_t offset;
    size_t size;
} IdleBuffer;

struct MyStreamSource : StreamSource {
public:
    MyStreamSource(void) {
        aviableBuffer.clear();
        pthread_mutex_init(&m_mutex, nullptr);
    }
    virtual ~MyStreamSource(void) {
        aviableBuffer.clear();
        pthread_mutex_destroy(&m_mutex);
    }
    //新增接口
    int GetAvailableBuffer(IdleBuffer* buffer)
    {
        pthread_mutex_lock(&m_mutex);
        if (aviableBuffer.empty()) {
            pthread_mutex_unlock(&m_mutex);
            return -1;
        }
        *buffer = aviableBuffer[0];
        aviableBuffer.erase(aviableBuffer.begin());
        pthread_mutex_unlock(&m_mutex);
        return 0;
    }
    uint8_t* GetBufferAddress(size_t idx)
    {
        std::shared_ptr<StreamCallback> callback = m_callBack.lock();
        if (callback == nullptr)
            return nullptr;
        return callback->GetBuffer(idx);
    }
    void QueueBuffer(size_t index, size_t offset, size_t size, int64_t timestampUs, uint32_t flags)
    {
        std::shared_ptr<StreamCallback> callback = m_callBack.lock();
        if (callback == nullptr)
            return;
        callback->QueueBuffer(index, offset, size, timestampUs, flags);
    }
    //继承接口
    void OnBufferAvailable(size_t index, size_t offset, size_t size) {
        printf("----- OnBufferAvailable(): i/%d o/%d s/%d ----- \n",
            index, offset, size);
        IdleBuffer buffer;
        pthread_mutex_lock(&m_mutex);
        buffer.idx = index;
        buffer.offset = offset;
        buffer.size = size;
        aviableBuffer.push_back(buffer);
        pthread_mutex_unlock(&m_mutex);
    }
    void SetStreamCallback(const std::shared_ptr<StreamCallback> &callback) {
        printf("----- SetStreamCallback() ----- \n");
        m_callBack = callback;
    }
private:
    std::weak_ptr<StreamCallback> m_callBack;
    std::vector<IdleBuffer> aviableBuffer;
    pthread_mutex_t m_mutex;
};

class MyPlayerCallback : public PlayerCallback {
public:
    void OnPlaybackComplete() override {
        printf("----- OnPlaybackComplete() ----- \n");
    }
    void OnError(int32_t errorType, int32_t errorCode) override {
        printf("----- OnError(): et/%d ec/%d ----- \n", errorType, errorCode);
    }
    void OnInfo(int type, int extra) override {
        printf("----- OnInfo(): t/%d e/%d ----- \n", type, extra);
    }
    void OnVideoSizeChanged(int width, int height) override {
        printf("----- OnVideoSizeChanged(): %d x %d ----- \n", width, height);
    }
    void OnRewindToComplete() override {
        printf("----- OnRewindToComplete() ----- \n");
    }
};

int test_audio(int argc, char **argv)
{
    int ret;

    int fd = open(argv[1], O_RDONLY);
    printf("----->> open(%d) \n", fd);
    if (fd < 1)
        return -1;

    Player *player = new Player();

    printf("----->> step 1 \n");

    shared_ptr<MyPlayerCallback> mpc = make_shared<MyPlayerCallback>();
    player->SetPlayerCallback(mpc);

    printf("----->> step 2 \n");

    shared_ptr<MyStreamSource> mss = make_shared<MyStreamSource>();
    Format formats;
    formats.PutStringValue(CODEC_MIME, MIME_AUDIO_AAC);
    Source source(mss, formats);

    printf("----->> step 3 \n");

    ret = player->SetSource(source);
    printf("----->> player->SetSource(%d) \n", ret);

    ret = player->Prepare();
    printf("----->> player->Prepare(%d) \n", ret);

    // ret = player->EnableSingleLooping(true);
    // printf("----->> player->EnableSingleLooping(%d) \n", ret);

    // ret = player->SetVolume(1.0, 1.0);
    // printf("----->> player->SetVolume(%d) \n", ret);

    ret = player->Play();
    printf("----->> player->Play(%d) \n", ret);

    int timeout = 0;
    IdleBuffer buffer;
    uint8_t *data = nullptr;

    while(timeout++ < 100)
    {
        if (mss->GetAvailableBuffer(&buffer) == 0)
        {
            data = mss->GetBufferAddress(buffer.idx);
            if (data != nullptr)
            {
                ret = read(fd, data + buffer.offset, buffer.size);
                if (ret > 0)
                    mss->QueueBuffer(buffer.idx, buffer.offset, ret, 0, MyStreamCallback::STREAM_FLAG_PARTIAL_FRAME);
                else
                    lseek(fd, 0, SEEK_SET);
                printf("----->> read(%d) \n", ret);
                usleep(1000);
                continue;
            }
            else
                printf("-----> mss->GetBufferAddress: failed \n");
        }
        else
            printf("-----> mss->getAvailableBuffer: failed \n");

        usleep(100000);
    }

    ret = player->Stop();
    printf("----->> player->Stop(%d) \n", ret);

    ret = player->Release();
    printf("----->> player->Release(%d) \n", ret);

    close(fd);

    delete player;

    return 0;
}


int test_video(int argc, char **argv)
{
    int ret;
    Player *player = new Player();

    printf("----->> step 1 \n");

    shared_ptr<MyPlayerCallback> mpc = make_shared<MyPlayerCallback>();
    player->SetPlayerCallback(mpc);

    printf("----->> step 2 \n");

    std::string uri(argv[1]);
    Source source(uri);

    printf("----->> step 3 \n");

    ret = player->SetSource(source);
    printf("----->> player->SetSource(%d) \n", ret);

    ret = player->Prepare();
    printf("----->> player->Prepare(%d) \n", ret);

    int64_t duration;
    int width, height;

    player->GetDuration(duration);
    printf("----->> [%s,%d] GetDuration:%lld\n", __func__, __LINE__, duration);
    player->GetVideoWidth(width);
    printf("----->> [%s,%d] width:%d\n", __func__, __LINE__, width);
    player->GetVideoHeight(height);
    printf("----->> [%s,%d] height:%d\n", __func__, __LINE__, height);

    if (height > 480)
    {
        width = (int)(width * (480 / (float)height));
        height = 480;
    }

    Surface *surface = Surface::CreateSurface();
    if (surface == nullptr) {
        printf("[%s,%d] CreateSurface failed\n",  __func__, __LINE__);
        return -1;
    }
    surface->SetUserData("region_position_x", "0");
    surface->SetUserData("region_position_y", "0");
    surface->SetUserData("region_width", to_string(width));
    surface->SetUserData("region_height", to_string(height));
    player->SetVideoSurface(surface);

    // ret = player->EnableSingleLooping(true);
    // printf("----->> player->EnableSingleLooping(%d) \n", ret);

    // ret = player->SetVolume(1.0, 1.0);
    // printf("----->> player->SetVolume(%d) \n", ret);

    ret = player->Play();
    printf("----->> player->Play(%d) \n", ret);

    sleep(15);

    ret = player->Stop();
    printf("----->> player->Stop(%d) \n", ret);

    ret = player->Release();
    printf("----->> player->Release(%d) \n", ret);

    delete player;
    return 0;
}

int main(int argc, char **argv)
{
    // return argc > 2 ? test_video(argc, argv) : test_audio(argc, argv);
    return test_audio(argc, argv); // own file "audio/aac"
}
